home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / cpp_libs / intrvews / xgrab.lha / xgrab / ui / gview.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-04-24  |  27.0 KB  |  1,326 lines

  1. /**
  2.    GRAB Graph Layout and Browser System
  3.  
  4.    Copyright (c) 1987, 1988, 1989 Stanford University
  5.    Copyright (c) 1989, Tera Computer Company
  6.  **/
  7.  
  8.   /**
  9.      Implementation of the graph area.  
  10.   
  11.      Lots of neat trickiness in here.  Most notably:  force the graph
  12.      to be redrawn when the window is resized.  Stop the graph from being
  13.      redrawn superfluously.  Perspective fu.  Mouse Button fu.  Moving
  14.      rectangles and line fu.  Joe Bob says check it out.
  15.   **/
  16.  
  17. #include <InterViews/brush.h>
  18. #include <InterViews/canvas.h>
  19. #include <InterViews/event.h>
  20. #include <InterViews/interactor.h>
  21. #include <InterViews/painter.h>
  22. #include <InterViews/perspective.h>
  23. #include <InterViews/rubband.h>
  24. #include <InterViews/rubline.h>
  25. #include <InterViews/rubrect.h>
  26. #include <InterViews/scene.h>
  27. #include <InterViews/sensor.h>
  28. #include <InterViews/shape.h>
  29. #include <string.h>
  30. #include <math.h>
  31. #include "graph.h"
  32. #include "gview.h"
  33. #include "gframe.h"
  34. #include "routines.h"
  35. #include "curs.h"
  36.  
  37. static const defaultwidth = 600;
  38. static int defaultheight = 400;
  39.  
  40. GView::GView (Graph* g) : (allEvents, nil)
  41. {
  42.     perspective = new Perspective();
  43.     graph = g;
  44.     g->SetGB(this);        /* tell the graph who we are */
  45.     frame = nil;
  46.     currno = 0;
  47.     okaytodraw = false;
  48.     resizedone = false;
  49.     firsttime = true;
  50.     Init();
  51. }
  52.  
  53. void GView::Init ()
  54. {
  55.     shape->width = defaultwidth;
  56.     shape->height = (int) ((double) shape->width / GetAspRatio());
  57.     shape->hshrink = hfil;
  58.     shape->hstretch = hfil;
  59.     shape->vshrink = vfil;
  60.     shape->vstretch = vfil;
  61.     x0 = 0;
  62.     y0 = 0;
  63.     mag = 1.0;
  64.     FixPerspective(perspective);
  65. }
  66.  
  67. void GView::FixPerspective(Perspective* p)
  68.   /* make sure the perspective has the proper values */
  69. {
  70.     p->x0 = 0;
  71.     p->y0 = 0;
  72.  
  73.     if (canvas != nil)
  74.     {
  75.         p->curwidth = canvas->Width();
  76.         p->curheight = canvas->Height();
  77.     }
  78.     else
  79.     {
  80.     p->curwidth = defaultwidth;
  81.         p->curheight = (int) ((double) p->curwidth / GetAspRatio());
  82.     }
  83.  
  84.     p->width = round (p->curwidth * mag);
  85.     p->height = round (p->curheight * mag);
  86.     p->curx = -x0;
  87.     p->cury = -y0;
  88. }
  89.  
  90. void GView::SetFrame(GraphFrame* f)
  91.   /* so we can pick on the one upstairs */
  92. {
  93.     frame = f;
  94. }
  95.  
  96. Canvas *GView::GetCanvas()
  97. {
  98.     return canvas;
  99. }
  100.  
  101. void GView::UpdateP () 
  102. {
  103.     okaytodraw = true;        /* you asked for it */
  104.     Interactor::Update();
  105. }
  106.  
  107. void GView::UpdatePerspective ()
  108. {
  109.     if (okaytodraw)
  110.     {
  111.         perspective->Update();
  112.     }
  113. }
  114.  
  115. void GView::UpdateG()
  116. {
  117.     okaytodraw = true;        /* you asked for it */
  118.     Update();
  119. }
  120.  
  121. void GView::Update() 
  122. {
  123.     UpdatePerspective();
  124.     firsttime = true;    /* if we've called it, we don't want to call DoGraph */
  125.     Draw();
  126. }
  127.  
  128.   /**
  129.      Draw and Redraw:  We only want to do this if it's okay to draw and the
  130.      canvas has been mapped i.e. != nil.
  131.     
  132.      Draw and Redraw are called from many places, including after a resize is
  133.      done, so if we set a flag there, we know here that we have to call
  134.      DoGraph.  On each window resize, the Resize procedure seems to be called
  135.      many times, so we don't want to call DoGraph from there.
  136.     
  137.      There's no intelligent way to redraw just part of the screen, so just
  138.      do as Draw does.
  139.  
  140.      firsttime is sort of a misnomer.  It distinguishes between calls to
  141.      Draw by Update i.e. by UpdateG i.e. by the user and calls by mysterious
  142.      parts of the InterViews software.
  143.    **/
  144. void GView::Draw ()
  145. {
  146.     if (okaytodraw && canvas != nil)
  147.     {
  148.         output->ClearRect(canvas, 0, 0, xmax, ymax);
  149.  
  150.     if (firsttime || !resizedone)
  151.     {
  152.         firsttime = false;
  153.         resizedone = false;
  154.         graph->DrawAll();
  155.     }
  156.     else
  157.     {
  158.         resizedone = false;
  159.         DoGraph();
  160.     }
  161.     }
  162. }
  163.  
  164. void GView::Redraw (Coord a, Coord b, Coord c, Coord d)
  165. {
  166.     if (okaytodraw && canvas != nil)
  167.     {
  168.     output->ClearRect(canvas, a, b, c, d);
  169.  
  170.     if (firsttime || !resizedone)
  171.     {
  172.         firsttime = false;
  173.         resizedone = false;
  174.         graph->DrawAll();
  175.     }
  176.     else
  177.     {
  178.         resizedone = false;
  179.         DoGraph();
  180.     }
  181.     }
  182. }
  183.  
  184. void GView::Erase() 
  185. {
  186.     okaytodraw = false;      
  187.       /** 
  188.      inhibit redrawing the graph.  After an erase, DoGraph will be called,
  189.      which will redraw the graph.  Between that time, any extra calls
  190.      to draw the graph are pointless
  191.        **/
  192.     graph->DeleteAll();
  193. }
  194.  
  195. void GView::CenterGraph ()
  196. {
  197.     Perspective p;
  198.  
  199.     okaytodraw = false;
  200.       /**
  201.      after we center, UpdateG is eventually called.  Any intervening
  202.      calls to draw the graph are pointless
  203.        **/
  204.  
  205.       /* get the proper perspective */
  206.     p = *GetPerspective();
  207.     mag = 1.0;
  208.     x0 = 0;
  209.     y0 = 0;
  210.     FixPerspective(&p);
  211.     Adjust(p);
  212.  
  213.        /* Adjust changes the following values, so reset them */
  214.     mag = 1.0;
  215.     x0 = 0;
  216.     y0 = 0;
  217. }
  218.  
  219. void GView::FocusGraph(int x, int y, float m)
  220.   /**
  221.      Magnify the graph by m, and translate it so (x, y) is the center
  222.    **/
  223. {
  224.     Perspective p;
  225.  
  226.     okaytodraw = false;
  227.       /**
  228.      after we focus, DoGraph is called.  Any intervening
  229.      calls to draw the graph are pointless
  230.        **/
  231.  
  232.     p = *GetPerspective();
  233.     p.curwidth = round(p.curwidth/m);
  234.     p.curheight = round(p.curheight/m);
  235.     Adjust(p);
  236.  
  237.     p = *GetPerspective();
  238.     p.curx = round(m * x - canvas->Width()/2);
  239.     p.cury = round(m * y - canvas->Height()/2);
  240.     Adjust(p);
  241.  
  242.     okaytodraw = true;
  243.     DoGraph();
  244. }
  245.  
  246. void GView::ChangePGrad(int newx, int newy)
  247. {
  248.     Perspective *p = GetPerspective();
  249.  
  250.     p->sx = newx;
  251.     p->sy = newy;
  252.     p->lx = 2 * newx;
  253.     p->ly = 2 * newy;
  254. }
  255.  
  256. void GView::GetBounds(int* bx, int* by, int* bwidth, int* bheight, 
  257.                   int* bcurx, int* bcury, int* bcurwidth, int* bcurheight)
  258. {
  259.     Perspective* p = GetPerspective();
  260.  
  261.     *bx = p->x0;
  262.     *by = p->y0;
  263.     *bwidth = p->width;
  264.     *bheight = p->height;
  265.     *bcurx = p->curx;
  266.     *bcury = p->cury;
  267.     *bcurwidth = p->curwidth;
  268.     *bcurheight = p->curheight;
  269. }
  270.  
  271. void GView::Resize()
  272.   /** 
  273.      Undo the damage done by the Interactor Resize routine, which resets the
  274.      pan gradients.  Also, set the flag to indicate a resize was done. 
  275.      Eventually the top-level Resize routine calls Draw, and we'll know
  276.      then to erase the graph and start over as opposed to just using
  277.      the graph we have now
  278.    **/
  279. {
  280.     Perspective q = *GetPerspective();
  281.  
  282.     Interactor::Resize();
  283.     Perspective *p = GetPerspective();
  284.     FixPerspective(p);
  285.     p->sx = q.sx;
  286.     p->lx = q.lx;
  287.     p->sy = q.sy;
  288.     p->ly = q.ly;
  289.  
  290.     Perspective np = *p;
  291.  
  292.     if (q != np)
  293.     {
  294.       /* something's changed */
  295.         resizedone = true;
  296.     }
  297. }
  298.  
  299. void GView::SetFHandler(FontHandler* f)
  300. {
  301.     graph->SetFHandler(f);
  302. }
  303.  
  304. static const slack = 3;
  305.   /* for those of us who can't pinpoint a pixel with the mouse */
  306.  
  307. void GView::ResetCurrent()
  308.   /**
  309.      There's a good chance the list of items under the cursor has
  310.      changed, so rebuild it
  311.    **/
  312. {
  313.     if (current != nil)
  314.     {
  315.         delete[currno] current;
  316.         current = nil;
  317.         currno = 0;
  318.     }
  319.  
  320.     FindIntersect();
  321. }
  322.  
  323.   /**
  324.      In an effort to allow all the functionality of all 3 modes at once, 
  325.      you can hold down keys while pressing a mouse button to change the current
  326.      mode.  Originally, the shift key indicated change mode, the meta key
  327.      indicated browse mode, and the control key indicated edit mode.
  328.   
  329.      Unfortunately, some window managers use these keys themselves, and
  330.      such events never get to us.  To further refine the idea, I decided to use 
  331.      surrounding keys in the same way.  So:  
  332.          z, Z, ?, /, shift    -- change mode
  333.          space bar, meta    -- browse mode
  334.          a, A, ", ', control    -- edit mode
  335.      (equal treatment for lefties, if you notice).
  336.   
  337.      Unfortunately, whoever wrote InterViews didn't support 
  338.      KeyRelease events, so I can't tell when the normal keys are released. 
  339.      Their reason is that some keyboards don't notify the system of KeyRelease
  340.      events.  With that kind of reasoning, I'm surprised the thing supports
  341.      colors.
  342.   
  343.      Anyway, the results are predictable:  press Z, and you'll be in change
  344.      mode until you press another key.  And so on.
  345.    **/
  346.  
  347. static char* oldkey = "\0";
  348.  
  349. boolean GView::BKeyPressed(Event& e)
  350. {
  351.     return (e.meta || !strcmp(oldkey, " "));
  352. }
  353.  
  354. boolean GView::CKeyPressed(Event& e)
  355. {
  356.     return (e.shift || !strcmp(oldkey, "/") || 
  357.         !strcmp(oldkey, "z") || !strcmp(oldkey, "?") || 
  358.         !strcmp(oldkey, "Z"));
  359. }
  360.  
  361. boolean GView::EKeyPressed(Event& e)
  362. {
  363.     return (e.control || !strcmp(oldkey, "\'") || 
  364.         !strcmp(oldkey, "a") || !strcmp(oldkey, "\"") || 
  365.         !strcmp(oldkey, "A"));
  366. }
  367.  
  368. void GView::Handle (Event& e) 
  369. {
  370.     ModeType buttonMode;
  371.  
  372.     if (e.eventType == UpEvent || e.eventType == DownEvent || 
  373.         e.eventType == MotionEvent || e.eventType == KeyEvent && 
  374.     e.target == this)
  375.     {
  376.         cursorx = e.x;
  377.         cursory = e.y;
  378.     ResetCurrent();
  379.  
  380.         if (e.eventType == MotionEvent)
  381.     {
  382.         DoMouseMove(e);
  383.     }
  384.     else
  385.     {
  386.           /** 
  387.          note what happens in the following when more than one "key"
  388.              is pressed:  The mode first in the if statement will win.
  389.              I figured change mode was the most innocuous, and edit mode
  390.              the most deadly, and wishing to avoid drastic inadvertent 
  391.              changes, I put change mode first and edit mode last 
  392.            **/
  393.         if (CKeyPressed(e) || 
  394.             (GetCModeFlag() && !(EKeyPressed(e) || BKeyPressed(e))))
  395.         {
  396.             buttonMode = cmode;
  397.         }
  398.         else if (BKeyPressed(e) || 
  399.              (GetBModeFlag() && !EKeyPressed(e)))
  400.         {
  401.             buttonMode = bmode;
  402.         }
  403.         else 
  404.         {
  405.             buttonMode = emode;
  406.         }
  407.  
  408.             switch (e.eventType)
  409.             {
  410.             case UpEvent:
  411.                 switch (e.button)
  412.                 {
  413.                     case RIGHTMOUSE:
  414.                     DoRightUp(e, buttonMode);
  415.                     break;
  416.                     case MIDDLEMOUSE:
  417.                     DoMiddleUp(e, buttonMode);
  418.                     break;
  419.                     case LEFTMOUSE:
  420.                     DoLeftUp(e, buttonMode);
  421.                     break;
  422.                 }
  423.                 break;
  424.             case DownEvent:
  425.                 switch (e.button)
  426.                 {
  427.                     case RIGHTMOUSE:
  428.                     DoRightDown(e, buttonMode);
  429.                     break;
  430.                     case MIDDLEMOUSE:
  431.                 DoMiddleDown(e, buttonMode);
  432.                 break;
  433.                 case LEFTMOUSE:
  434.                 DoLeftDown(e, buttonMode);
  435.                 break;
  436.                 }
  437.                 break;
  438.         case KeyEvent:
  439.             delete oldkey;
  440.             oldkey = new char[e.len];
  441.             strcpy (oldkey, e.keystring);
  442.             break;
  443.             default:
  444.                 break;
  445.         }
  446.         }
  447.     }
  448. }
  449.  
  450. void GView::FindIntersect()
  451.   /**
  452.      find which graphics objects are under the cursor.  Make the cursor
  453.      into a small box instead of a point so we don't have to be 
  454.      Joe Dexterity (Jane Dexterity?)
  455.    **/
  456. {
  457.     Coord tx1, tx2, ty1, ty2;
  458.  
  459.     if (currno == 0)
  460.     {
  461.     tx1 = cursorx + slack;
  462.     tx2 = cursorx - slack;
  463.     ty1 = cursory + slack;
  464.     ty2 = cursory - slack;
  465.         BoxObj b = BoxObj(tx1, ty1, tx2, ty2);
  466.         currno = graph->myGraphicsIntersecting(b, current);
  467.     }
  468. }
  469.  
  470. void GView::translate (Coord x, Coord y, Coord* tx, Coord* ty)
  471.   /* this isn't ever called, anyway */
  472. {
  473.     int sx0, sy0, swidth, sheight;
  474.  
  475.     Perspective *p = GetPerspective();
  476.     GetSBounds(&sx0, &sy0, &swidth, &sheight);
  477.  
  478. /* Change to (0,1) bounding box */
  479.     float ix = (float) (x + p->curx) / (float) p->width;
  480.     float iy = (float) (y + p->cury) / (float) p->height;
  481. /* Change to screen coordinate system */
  482.     *tx = round(ix * swidth - sx0);
  483.     *ty = round(iy * sheight - sy0);
  484. }
  485.  
  486.  
  487. void GView::invtranslate (Coord x, Coord y, Coord* tx, Coord* ty)
  488.   /* neither is this */
  489. {
  490.     int sx0, sy0, swidth, sheight;
  491.  
  492.     Perspective *p = GetPerspective();
  493.     GetSBounds(&sx0, &sy0, &swidth, &sheight);
  494.  
  495. /* Change to (0,1) bounding box */
  496.     float ix = (float) (x + sx0) / (float) swidth;
  497.     float iy = (float) (y + sy0) / (float) sheight;
  498. /* Change to screen coordinate system */
  499.     *tx = round(ix * p->width - p->curx);
  500.     *ty = round(iy * p->height - p->cury);
  501. }
  502.  
  503. boolean GView::CurOnNonDummyNode()
  504.   /* are we on a non-dummy node? */
  505. {
  506.     int i;
  507.  
  508.     FindIntersect();
  509.     for (i = 0; i < currno; i++)
  510.     {
  511.     if (current[i]->gdtype == node)
  512.     {
  513.         return true;
  514.     }
  515.     }
  516.  
  517.     return false;
  518. }
  519.  
  520. boolean GView::CurOnNode()
  521.   /* are we on a node */
  522. {
  523.     int i;
  524.  
  525.     FindIntersect();
  526.     for (i = 0; i < currno; i++)
  527.     {
  528.     if ((current[i]->gdtype == node) || (current[i]->gdtype == dummy))
  529.     {
  530.         return true;
  531.     }
  532.     }
  533.  
  534.     return false;
  535. }
  536.  
  537. boolean GView::CurOnEdge()
  538.   /* are we on an edge? */
  539. {
  540.     int i;
  541.  
  542.     FindIntersect();
  543.     for (i = 0; i < currno; i++)
  544.     {
  545.     if (current[i]->gdtype == edge)
  546.     {
  547.         return true;
  548.     }
  549.     }
  550.  
  551.     return false;
  552. }
  553.  
  554. char *GView::CurNode()
  555.   /**
  556.      what node are we on? 
  557.      Note: there should be a CurNonDummyNode.  Where is it?
  558.    **/
  559. {
  560.     int i;
  561.     
  562.     for (i = 0; i < currno; i++)
  563.     {
  564.     if ((current[i]->gdtype == node) || (current[i]->gdtype == dummy))
  565.     {
  566.         return current[i]->gpart;
  567.     }
  568.     }
  569.  
  570.     return nil;
  571. }
  572.  
  573. char *GView::CurEdge()
  574.   /* what edge are we on? */
  575. {
  576.     int i;
  577.     
  578.     for (i = 0; i < currno; i++)
  579.     {
  580.     if (current[i]->gdtype == edge)
  581.     {
  582.         return current[i]->gpart;
  583.     }
  584.     }
  585.  
  586.     return nil;
  587. }
  588.  
  589. static Coord wid, ht;
  590.  
  591. void GView::SetupMove(char *mnode, char* prev[], char* next[], int numprev, 
  592.     int numnext)
  593.   /* we're about to do a move */
  594. {
  595.     graph->SetupMove(mnode, prev, next, numprev, numnext, 
  596.              &wid, &ht);
  597. }
  598.  
  599. void GView::DoMove(Event& e)
  600.   /**
  601.      Presumably, SetupMove has already been called.  Create a fake
  602.      node so the user can see it as it slides around.  When the button's
  603.      released, call DoMoveEnd
  604.  
  605.      The sliding rectangle code is inspired by the InterViews source 
  606.      for sliders
  607.    **/
  608. {
  609.     SlidingRect* r;
  610.  
  611.     DoSetUpMove();
  612.     Listen(allEvents);
  613.     r = new SlidingRect(output, canvas, e.x - wid, e.y - ht,
  614.             e.x + wid, e.y + ht, e.x, e.y);
  615.     r->Draw();
  616.  
  617.     do {
  618.     switch (e.eventType) 
  619.     {
  620.         case MotionEvent:
  621.         e.target->GetRelative(e.x, e.y, this);
  622.         Constrain(e);
  623.         r->Track(e.x, e.y);
  624.         break;
  625.         default:
  626.         break;
  627.     }
  628.  
  629.     Read(e);
  630.     } while (e.eventType != UpEvent || e.button != MIDDLEMOUSE);
  631.  
  632.     Constrain(e);    /* don't let the rectangle go off the screen */
  633.     r->Erase();
  634.     cursorx = e.x;
  635.     cursory = e.y;
  636.     Listen(input);
  637.     delete r;
  638.     DoMoveEnd();
  639. }
  640.  
  641. void GView::DoInsArc(Event& e)
  642.   /**
  643.      Inserting arcs is a little trickier, since you can't finish
  644.      with the arc just anywhere.  Assuming the starting node is
  645.      viable, keep inserting arcs until DoInsArcEnd says to stop
  646.    **/
  647. {
  648.     RubberLine* r;
  649.  
  650.     if (DoSetUpInsArc())    /* check the start node is okay */
  651.     {
  652.         do
  653.         {
  654.             Listen(allEvents);
  655.             r = new RubberLine(output, canvas, e.x, e.y, e.x, e.y);
  656.     
  657.             do 
  658.         {
  659.             switch (e.eventType) 
  660.             {
  661.                 case MotionEvent:
  662.                 e.target->GetRelative(e.x, e.y, this);
  663.                 Constrain(e);
  664.                 r->Track(e.x, e.y);
  665.                 break;
  666.                 default:
  667.                 break;
  668.             }
  669.         
  670.             Read(e);
  671.             } while (e.eventType != UpEvent || e.button != LEFTMOUSE);
  672.         
  673.             Constrain(e);    /* don't let the endpoint go off the screen */
  674.             r->Erase();
  675.         cursorx = e.x;
  676.         cursory = e.y;
  677.         ResetCurrent();    
  678.           /* need to know where we've ended up, so recalculate current */
  679.             Listen(input);
  680.         delete r;
  681.         } while (!DoInsArcEnd());
  682.     }
  683. }
  684.  
  685. void GView::Constrain(Event& e)
  686.   /* don't go outside the bounds (0, 0), (xmax, ymax) */
  687. {
  688.     e.x = min(max(e.x, 0), xmax);
  689.     e.y = min(max(e.y, 0), ymax);
  690. }
  691.  
  692. void GView::ChangeBC()
  693. {
  694.     graph->ChangeBC();
  695. }
  696.  
  697. void GView::ChangePA()
  698. {
  699.     graph->ChangePA();
  700. }
  701.  
  702. void GView::ChangeForceNL()
  703. {
  704.     graph->ChangeForceNL();
  705. }
  706.  
  707. void GView::ChangeForceEL()
  708. {
  709.     graph->ChangeForceEL();
  710. }
  711.  
  712. void GView::ChangeMarkDummy()
  713. {
  714.     graph->ChangeMarkDummy();
  715. }
  716.  
  717. void GView::ChangeCurNodeLabel(char* s)
  718.   /* find the current node, then change it's label to s */
  719. {
  720.     for (int i = 0; i < currno; i++)
  721.     {
  722.     if (current[i]->gdtype == node || current[i]->gdtype == dummy)
  723.     {
  724.         graph->ChangeNodeLabel(current[i]->gpart, s);
  725.         break;
  726.     }
  727.     }
  728. }
  729.  
  730. void GView::GetCurPos(int* px, int* py)
  731.   /* return the current cursor position */
  732. {
  733.     Perspective* p = GetPerspective();
  734.  
  735.     *px = cursorx;
  736.     *py = cursory;
  737. }
  738.  
  739.   /**
  740.      What follows are the routines to handle mouse button presses
  741.      and releases.  GetInCModeFlag, tell indicates if we're 
  742.      in a special mode.  In this case, other operations are verboten
  743.    **/
  744.  
  745. void GView::DoLeftDown(Event& e, ModeType m)
  746. {
  747.     if (GetInCModeFlag())
  748.     {
  749.     /* wait for button to be released */
  750.     }
  751.     else
  752.     {
  753.         switch (m)
  754.         {
  755.         case bmode:
  756.         case cmode:
  757.             break;
  758.  
  759.         case emode:
  760.             if (CurOnNode())
  761.               /* if on node, then insert arc */
  762.             {
  763.                 if (CurOnNonDummyNode())
  764.                 {
  765.                     DoInsArc(e);
  766.                     ResetCurrent();
  767.                     ResetCursor();
  768.                 }
  769.                 else
  770.                 {
  771.                 frame->ChangeStatusLine(
  772.                     "Must start edge on a non-dummy node.");
  773.                 }
  774.             }
  775.             else 
  776.             {
  777.                 DoSInsertNode();
  778.                 ResetCurrent();
  779.             }
  780.         break;
  781.     }
  782.     }
  783. }
  784.     
  785. void GView::DoLeftUp(Event& e, ModeType m)
  786. {
  787.     if (GetInCTextModeFlag()) 
  788.     {
  789.     if (CurOnNode())
  790.     {
  791.         DoChangeNodeText();
  792.     }
  793.     else 
  794.     {
  795.         frame->ChangeStatusLine(
  796.         "change node text: cursor must be on a node");
  797.     }
  798.  
  799.     ClearInModeFlag();
  800.     } 
  801.     else if (GetInCEdgeLabelModeFlag())
  802.     {
  803.     if (CurOnEdge())
  804.     {
  805.         DoChangeEdgeText();
  806.     }
  807.     else
  808.     {
  809.         frame->ChangeStatusLine(
  810.         "change edge label: cursor must be on an edge");
  811.     }
  812.  
  813.     ClearInModeFlag();
  814.     } 
  815.     else if (GetInFNodeModeFlag())
  816.     {
  817.     if (CurOnNode())
  818.     {
  819.         DoFocusCurNode();
  820.         ResetCurrent();
  821.     }
  822.     else
  823.     {
  824.         frame->ChangeStatusLine(
  825.         "focus node: cursor must be on a node");
  826.     }
  827.  
  828.     ClearInModeFlag();
  829.     }
  830.     else 
  831.     {
  832.     switch (m)
  833.     {
  834.         case emode:
  835.         break;
  836.         
  837.         case bmode:
  838.             if (CurOnNode()) 
  839.             {
  840.                 if (CurOnNonDummyNode())
  841.                 {
  842.             if (GetInEFlag() || GetOutEFlag())
  843.             {
  844.                         DoSetEdges();
  845.  
  846.                 if (GetInEFlag() && GetOutEFlag())
  847.                 {
  848.                             frame->ChangeStatusLine(
  849.                     "in and out edge attributes set");
  850.                 }
  851.                 else if (GetInEFlag())
  852.                 {
  853.                     frame->ChangeStatusLine(
  854.                     "in edge attributes set");
  855.                 }
  856.                 else 
  857.                 {
  858.                     frame->ChangeStatusLine(
  859.                     "out edge attributes set");
  860.                 }
  861.             }
  862.             else
  863.             {
  864.                 frame->ChangeStatusLine("No effect:  change in/out edge flags both off");
  865.             }
  866.                 }
  867.                 else
  868.                 {
  869.                 frame->ChangeStatusLine("set in/out edge attributes: must be on non-dummy node");
  870.                 }
  871.             } 
  872.             else 
  873.             {
  874.                 frame->ChangeStatusLine(
  875.                 "set in/out edge attributes: cursor must be on a node");
  876.             }
  877.         break;
  878.     
  879.         case cmode:
  880.         if (CurOnNode())
  881.         {
  882.             if (CurOnNonDummyNode())
  883.             {
  884.                 DoSetNode();
  885.                     frame->ChangeStatusLine("node attributes set");
  886.             }
  887.                 else
  888.                 {
  889.                 frame->ChangeStatusLine(
  890.                     "set node attributes: must be on non-dummy node");
  891.                 }
  892.         }
  893.         else if (CurOnEdge())
  894.         {
  895.             DoSetEdge();
  896.                 frame->ChangeStatusLine("edge attributes set");
  897.         }
  898.         else
  899.         {
  900.                 frame->ChangeStatusLine(
  901.                 "set attributes: cursor must be on a node or an edge ");
  902.         }
  903.     }
  904.     }
  905.  
  906.     ResetCursor();
  907. }
  908.  
  909. void GView::DoMiddleDown(Event& e, ModeType m)
  910. {
  911.     if (GetInCModeFlag())
  912.     {
  913.        /* ignore this button */
  914.     }
  915.     else 
  916.     {
  917.     switch (m)
  918.     {
  919.         case bmode:
  920.         case cmode:
  921.         break;
  922.  
  923.         case emode:
  924.             if (CurOnNode()) 
  925.             {
  926.                 DoMove(e);
  927.                 ResetCurrent();
  928.                 ResetCursor();
  929.             } 
  930.             else 
  931.             {
  932.                 frame->ChangeStatusLine(
  933.                 "Move must start on a node");
  934.             }
  935.         break;
  936.     }
  937.     } 
  938. }
  939.  
  940. void GView::DoMiddleUp(Event& e, ModeType m)
  941. {
  942.     if (GetInCModeFlag())
  943.     {
  944.        /* ignore this button */
  945.     }
  946.     else
  947.     {
  948.     switch (m)
  949.     {
  950.         case emode:
  951.             /* nothing to do */
  952.         break;
  953.  
  954.         case bmode:
  955.             if (CurOnNode()) 
  956.             {
  957.                 if (CurOnNonDummyNode())
  958.                 {
  959.             if (GetInEFlag() || GetOutEFlag())
  960.             {
  961.                         DoResetEdges();
  962.  
  963.                 if (GetInEFlag() && GetOutEFlag())
  964.                 {
  965.                             frame->ChangeStatusLine(
  966.                     "in and out edge attributes reset");
  967.                 }
  968.                 else if (GetInEFlag())
  969.                 {
  970.                     frame->ChangeStatusLine(
  971.                     "in edge attributes reset");
  972.                 }
  973.                 else 
  974.                 {
  975.                     frame->ChangeStatusLine(
  976.                     "out edge attributes reset");
  977.                 }
  978.             }
  979.             else
  980.             {
  981.                 frame->ChangeStatusLine("No effect:  change in/out edge flags both off");
  982.             }
  983.                 }
  984.                 else
  985.                 {
  986.                 frame->ChangeStatusLine("reset in/out edge attributes: must be on non-dummy node");
  987.                 }
  988.             } 
  989.             else
  990.             {
  991.                 frame->ChangeStatusLine("reset in/out edge attributes: cursor must be on a node");
  992.             }
  993.         break;
  994.  
  995.         case cmode:
  996.         if (CurOnNode())
  997.         {
  998.             if (CurOnNonDummyNode())
  999.             {
  1000.                 DoResetNode();
  1001.                     frame->ChangeStatusLine("node attributes reset");
  1002.             }
  1003.                 else
  1004.                 {
  1005.                 frame->ChangeStatusLine(
  1006.                     "reset node attributes: must be on non-dummy node");
  1007.                 }
  1008.         }
  1009.         else if (CurOnEdge())
  1010.         {
  1011.             DoResetEdge();
  1012.                 frame->ChangeStatusLine("edge attributes reset");
  1013.         }
  1014.         else
  1015.         {
  1016.                 frame->ChangeStatusLine(
  1017.                 "reset attributes: must be on a node or an edge");
  1018.         }
  1019.     }
  1020.     }
  1021.  
  1022.     ResetCursor();
  1023. }
  1024.  
  1025. void GView::DoRightDown(Event& e, ModeType m)
  1026. {
  1027.     if (GetInCModeFlag())
  1028.     {
  1029.         /* ignore this button */
  1030.     }
  1031.     else 
  1032.     {
  1033.     switch (m)
  1034.     {
  1035.         case bmode:
  1036.         case cmode:
  1037.         case emode:
  1038.                 /* wait for button to be released */
  1039.         break;
  1040.     }
  1041.     } 
  1042. }
  1043.  
  1044. void GView::DoRightUp(Event& e, ModeType m)
  1045. {
  1046.     if (GetInCModeFlag())
  1047.     {
  1048.         /* ignore this button */
  1049.     }
  1050.     else 
  1051.     {
  1052.     switch (m)
  1053.     {
  1054.         case emode:
  1055.         if (CurOnNode())
  1056.             {
  1057.                 DoSDeleteNode();
  1058.                 ResetCurrent();
  1059.             }
  1060.             else if (CurOnEdge()) 
  1061.             {
  1062.                 DoSDeleteArc();
  1063.                 ResetCurrent();
  1064.             }
  1065.             else
  1066.             {
  1067.                 frame->ChangeStatusLine (
  1068.                 "delete:  cursor must be on a node or edge");
  1069.             }
  1070.             break;
  1071.  
  1072.         case bmode:
  1073.             if (CurOnNonDummyNode())
  1074.             {
  1075.                 DoShowNAttr();
  1076.             } 
  1077.             else if (CurOnEdge())
  1078.             {
  1079.             DoShowEAttr();
  1080.             }
  1081.         else
  1082.         {
  1083.             DoEraseAttrBox();
  1084.         }
  1085.         break;
  1086.  
  1087.         case cmode:
  1088.         if (CurOnNode())
  1089.         {
  1090.             if (CurOnNonDummyNode())
  1091.             {
  1092.                 DoCycleNode();
  1093.                     frame->ChangeStatusLine("node attributes changed");
  1094.             }
  1095.                 else
  1096.                 {
  1097.                 frame->ChangeStatusLine("change node attributes: must be on non-dummy node");
  1098.                 }
  1099.         }
  1100.         else if (CurOnEdge())
  1101.         {
  1102.             DoCycleEdge();
  1103.                 frame->ChangeStatusLine("edge attributes changed");
  1104.         }
  1105.         else
  1106.         {
  1107.                 frame->ChangeStatusLine(
  1108.                 "change attributes: must be on a node or an edge ");
  1109.         }
  1110.         break;
  1111.     }
  1112.     }
  1113.  
  1114.     ResetCursor();
  1115. }
  1116.  
  1117.   /** 
  1118.      It used to be you could display the node label if you moved onto
  1119.      the node and an option was set.  I found this very annoying.  Besides,
  1120.      it didn't work.
  1121.    **/
  1122. static char* oldnode = nil;
  1123.  
  1124. void GView::DoMouseMove(Event& e)
  1125. {
  1126.     char* curnode = nil;
  1127.  
  1128.     if (CurOnNode())
  1129.     {
  1130.     curnode = CurNode();
  1131.  
  1132.     if (curnode != oldnode)
  1133.     {
  1134. //        MoveOn();
  1135.     }
  1136.     }
  1137.  
  1138.     if (curnode != oldnode && oldnode != nil)
  1139.     {
  1140. //    MoveOff(oldnode);
  1141.     }
  1142.  
  1143.     oldnode = curnode;
  1144.     ResetCursor();
  1145. }
  1146.  
  1147.   /**
  1148.      Set the cursor to its proper value 
  1149.    **/
  1150. void GView::ResetCursor()
  1151. {
  1152.     if (CurOnNonDummyNode())
  1153.     {
  1154.         frame->SetCurs(nodeC);
  1155.     }
  1156.     else if (CurOnEdge())
  1157.     {
  1158.     frame->SetCurs(lineC);
  1159.     }
  1160.     else if (CurOnNode()) /* dummy node */
  1161.     {
  1162.         frame->SetCurs(nodeC);
  1163.     }
  1164.     else 
  1165.     {
  1166.         frame->SetCurs(mainC);
  1167.     }
  1168. }
  1169.  
  1170.   /**
  1171.      The following six routines are mostly stolen from the InterViews source
  1172.      for the GraphicBlock class
  1173.    **/
  1174. void GView::Normalize (Perspective& np) 
  1175. {
  1176.     register Perspective* p = perspective;
  1177.     float hfactor, vfactor;
  1178.  
  1179.     if (p->width != np.width) 
  1180.     {
  1181.     hfactor = float(p->width) / float(np.width);
  1182.     np.x0 = round(hfactor * float(np.x0));
  1183.     np.width = p->width;
  1184.     np.curx = round(hfactor * float(np.curx));
  1185.     np.curwidth = round(hfactor * float(np.curwidth));
  1186.     np.sx = round(hfactor * float(np.sx));
  1187.     }
  1188.  
  1189.     if (p->height != np.height) 
  1190.     {
  1191.     vfactor = float(p->height) / float(np.height);
  1192.     np.y0 = round(vfactor * float(np.y0));
  1193.     np.height = p->height;
  1194.     np.cury = round(vfactor * float(np.cury));
  1195.     np.curheight = round(vfactor * float(np.curheight));
  1196.     np.sy = round(vfactor * float(np.sy));
  1197.     }
  1198. }
  1199.  
  1200. float GView::ScaleFactor (Perspective& np) 
  1201. {
  1202.     register Perspective* p = perspective;
  1203.     float factor = 1;
  1204.     Coord dx, dy;
  1205.  
  1206.     dx = abs(p->curwidth - np.curwidth);
  1207.     dy = abs(p->curheight - np.curheight);
  1208.  
  1209.     if (dx < dy) 
  1210.     {
  1211.     factor = float(p->curwidth) / float(np.curwidth);
  1212.     }
  1213.     else 
  1214.     {
  1215.     factor = float(p->curheight) / float(np.curheight);
  1216.     }
  1217.  
  1218.     return factor;
  1219. }
  1220.  
  1221. void GView::Zoom (Perspective& np) 
  1222. {
  1223.     register Perspective* p = perspective;
  1224.     Coord cx, cy, halfw, halfh, dx, dy;
  1225.     float factor = ScaleFactor(np);
  1226.  
  1227.     if (factor != 1.0) 
  1228.     {
  1229.     cx = np.curx + np.curwidth/2;
  1230.     cy = np.cury + np.curheight/2;
  1231.     halfw = p->curwidth/2;
  1232.     halfh = p->curheight/2;
  1233.     dx = (p->curx + halfw) - cx;
  1234.     dy = (p->cury + halfh) - cy;
  1235.  
  1236.     x0 = round((x0 + dx - halfw)*factor + halfw);
  1237.     y0 = round((y0 + dy - halfh)*factor + halfh);
  1238.     p->width = round(p->width * factor);
  1239.     p->height = round(p->height * factor);
  1240.     p->curx = round(float(cx) * factor) - halfw;
  1241.     p->cury = round(float(cy) * factor) - halfh;
  1242.     mag *= factor;
  1243.     }
  1244. }
  1245.  
  1246. void GView::Scroll (Perspective& np) 
  1247. {
  1248.     register Perspective* p = perspective;
  1249.     Coord dx, dy;
  1250.  
  1251.     dx = p->curx - np.curx;
  1252.     dy = p->cury - np.cury;
  1253.     x0 += dx;
  1254.     y0 += dy;
  1255.     p->curx = np.curx;
  1256.     p->cury = np.cury;
  1257. }
  1258.  
  1259. void GView::Adjust (Perspective& np) 
  1260. {
  1261.     register Perspective* p = perspective;
  1262.     Perspective ptmp;
  1263.     
  1264.     if (*p != np) 
  1265.     {
  1266.     if (okaytodraw)
  1267.     {
  1268.        frame->ChangeStatusLine("Adjusting view of graph", true);
  1269.     }
  1270.  
  1271.     Normalize(np);
  1272.     ptmp = *p;
  1273.  
  1274.     if (np.curwidth != canvas->Width() || np.curheight!=canvas->Height()) 
  1275.     {
  1276.         Zoom(np);
  1277.     }
  1278.     else 
  1279.     {
  1280.         Scroll(np);
  1281.     }
  1282.  
  1283.     if (okaytodraw)
  1284.     {
  1285.         p->Update();
  1286.  
  1287.         if (ptmp != *p) 
  1288.         {
  1289.             DoGraph();
  1290.         }
  1291.     }
  1292.     }
  1293. }
  1294.  
  1295. void GView::SetMagnification (float m) 
  1296. {
  1297.     register Perspective* p = perspective;
  1298.     float factor;
  1299.     Coord cx, cy, halfw, halfh;
  1300.  
  1301.     factor = m/mag;
  1302.  
  1303.     if (factor != 1.0) 
  1304.     {
  1305.     halfw = p->curwidth/2;
  1306.     halfh = p->curheight/2;
  1307.     cx = p->curx + halfw;
  1308.     cy = p->cury + halfh;
  1309.  
  1310.     x0 = round((x0 - halfw)*factor + halfw);
  1311.     y0 = round((y0 - halfh)*factor + halfh);
  1312.     p->width = round(p->width * factor);
  1313.     p->height = round(p->height * factor);
  1314.     p->curx = round(float(cx) * factor) - halfw;
  1315.     p->cury = round(float(cy) * factor) - halfh;
  1316.  
  1317.     mag = m;
  1318.  
  1319.     if (okaytodraw)
  1320.     {
  1321.         p->Update();
  1322.         DoGraph();
  1323.     }
  1324.     }
  1325. }
  1326.